#ifndef   M_Code_PROJECT_H
#define   M_Code_PROJECT_H

#include <wx/datetime.h>
#include <wx/dynarray.h>
#include <wx/hashmap.h>
#include <wx/treectrl.h>
#include "misctreeitemdata.h"
#include "compiletargetbase.h"
#include "M_Code_plugin.h"
#include "projectbuildtarget.h"
#include "projectmanager.h"
#include <map>
#include <vector>


class M_Code_Project;
class ProjectBuildTarget;
class ProjectFile;
class FilesGroupsAndMasks;
class TiXmlNode;
class TiXmlElement;


WX_DECLARE_STRING_HASH_MAP( ProjectFile*, ProjectFiles );

typedef std::map<wxString, wxArrayString> VirtualBuildTargetsMap;

class FileTreeData : public MiscTreeItemData {
  public:
    enum FileTreeDataKind {
      ftdkUndefined = 0,
      ftdkProject,
      ftdkFolder,
      ftdkFile,
      ftdkVirtualGroup,
      ftdkVirtualFolder
    };

    FileTreeData( M_Code_Project* project, FileTreeDataKind kind = ftdkUndefined )
      : m_Index( -1 ),
        m_Project( project ),
        m_file( nullptr ),
        m_kind( kind ) {}

    FileTreeDataKind GetKind() const { return m_kind; }
    M_Code_Project* GetProject() const { return m_Project; }
    int GetFileIndex() const { return m_Index; }
    ProjectFile* GetProjectFile() const { return m_file; }
    const wxString& GetFolder() const { return m_folder; }
    void SetKind( FileTreeDataKind kind ) { m_kind = kind; }
    void SetProject( M_Code_Project* project ) { m_Project = project; }
    void SetFileIndex( int index ) { m_Index = index; }
    void SetProjectFile( ProjectFile* file ) { m_file = file; }
    void SetFolder( const wxString& folder ) { m_folder = folder; }
  private:
    int m_Index;
    M_Code_Project* m_Project;
    ProjectFile* m_file;
    wxString m_folder;
    FileTreeDataKind m_kind;
};


enum PCHMode {
  pchSourceDir = 0,
  pchObjectDir,
  pchSourceFile
};

class M_Code_Project : public CompileTargetBase {
  public:
    M_Code_Project( const wxString& filename = wxEmptyString );
    ~M_Code_Project();
    bool IsLoaded() const { return m_Loaded; }
    void ClearAllProperties();
    void CalculateCommonTopLevelPath();
    wxString GetCommonTopLevelPath() const;
    bool GetModified() const;
    void SetModified( bool modified = true );
    ProjectFile* GetFile( int index );
    ProjectFile* GetFileByFilename( const wxString& filename, bool isRelative = true, bool isUnixFilename = false );
    int GetFilesCount() { return m_Files.size(); }
    void SetMakefile( const wxString& makefile ) { m_Makefile = makefile; SetModified( true ); }
    const wxString& GetMakefile();
    void SetMakefileCustom( bool custom );
    bool IsMakefileCustom() { return m_CustomMakefile; }
    void SetMakefileExecutionDir( const wxString& dir );
    wxString GetMakefileExecutionDir();
    wxString GetExecutionDir();
    bool BuildTargetValid( const wxString& name, bool virtuals_too = true ) const;
    wxString GetFirstValidBuildTargetName( bool virtuals_too = true ) const;
    const wxString& GetDefaultExecuteTarget() const;
    void SetDefaultExecuteTarget( const wxString& name );
    int GetBuildTargetsCount() { return m_Targets.GetCount(); }
    ProjectBuildTarget* GetBuildTarget( int index );
    ProjectBuildTarget* GetBuildTarget( const wxString& targetName );
    ProjectBuildTarget* AddBuildTarget( const wxString& targetName );
    bool RenameBuildTarget( int index, const wxString& targetName );
    bool RenameBuildTarget( const wxString& oldTargetName, const wxString& newTargetName );
    ProjectBuildTarget* DuplicateBuildTarget( int index, const wxString& newName = wxEmptyString );
    ProjectBuildTarget* DuplicateBuildTarget( const wxString& targetName, const wxString& newName = wxEmptyString );
    bool ExportTargetAsProject( int index );
    bool ExportTargetAsProject( const wxString& targetName );
    bool RemoveBuildTarget( int index );
    bool RemoveBuildTarget( const wxString& targetName );
    void ReOrderTargets( const wxArrayString& nameOrder );
    bool SetActiveBuildTarget( const wxString& name );
    const wxString& GetActiveBuildTarget() const;
    PCHMode GetModeForPCH() const { return m_PCHMode; }
    void SetModeForPCH( PCHMode mode ) { m_PCHMode = mode; SetModified( true ); }
    void SetCompilerID( const wxString& id );
    wxTreeItemId GetProjectNode() { return m_ProjectNode; }
    void SetProjectNode( wxTreeItemId node ) { m_ProjectNode = node; }
    bool QueryCloseAllFiles();
    bool CloseAllFiles( bool dontsave = false );
    bool SaveAllFiles();
    bool Save();
    bool SaveAs();
    bool SaveLayout();
    bool LoadLayout();
    void BeginAddFiles();
    void EndAddFiles();
    void BeginRemoveFiles();
    void EndRemoveFiles();
    ProjectFile* AddFile( const wxString& targetName, const wxString& filename, bool compile = true, bool link = true, unsigned short int weight = 50 );
    ProjectFile* AddFile( int targetIndex, const wxString& filename, bool compile = true, bool link = true, unsigned short int weight = 50 );
    bool RemoveFile( ProjectFile* pf );
    struct Glob {
      wxString m_Path;
      wxString m_WildCard;
      bool m_Recursive;
      Glob( const wxString& path, const wxString& wildCard, bool recursive ) : m_Path( path ), m_WildCard( wildCard ), m_Recursive( recursive ) {}
    };
    void SetGlobs( const std::vector<Glob>& globs );
    std::vector<Glob> GetGlobs() const;
    const wxArrayString& ExpandedNodes() { return m_ExpandedNodes; }
    void AddExpandedNode( const wxString& path ) { m_ExpandedNodes.Add( path ); }
    void SaveTreeState( wxTreeCtrl* tree );
    void RestoreTreeState( wxTreeCtrl* tree );
    int SelectTarget( int initial = -1, bool evenIfOne = false );
    ProjectBuildTarget* GetCurrentlyCompilingTarget() { return m_CurrentlyCompilingTarget; }
    void SetCurrentlyCompilingTarget( ProjectBuildTarget* bt );
    bool DefineVirtualBuildTarget( const wxString& alias, const wxArrayString& targets );
    bool HasVirtualBuildTarget( const wxString& alias ) const;
    bool RemoveVirtualBuildTarget( const wxString& alias );
    wxArrayString GetVirtualBuildTargets() const;
    const wxArrayString& GetVirtualBuildTargetGroup( const wxString& alias ) const;
    wxArrayString GetExpandedVirtualBuildTargetGroup( const wxString& alias ) const;
    bool CanAddToVirtualBuildTarget( const wxString& alias, const wxString& target );
    const wxArrayString& GetVirtualFolders() const;
    void SetVirtualFolders( const wxArrayString& folders );
    bool AppendUniqueVirtualFolder( const wxString &folder );
    void RemoveVirtualFolders( const wxString &folder );
    void ReplaceVirtualFolder( const wxString &oldFolder, const wxString &newFolder );
    wxDateTime GetLastModificationTime() const { return m_LastModified; }
    void Touch();
    void SetExtendedObjectNamesGeneration( bool ext );
    bool GetExtendedObjectNamesGeneration() const;
    void SetNotes( const wxString& notes );
    const wxString& GetNotes() const;
    void SetShowNotesOnLoad( bool show );
    bool GetShowNotesOnLoad() const;
    void SetCheckForExternallyModifiedFiles( bool check );
    bool GetCheckForExternallyModifiedFiles() const;
    void ShowNotes( bool nonEmptyOnly, bool editable = false );
    virtual void SetTitle( const wxString& title );
    virtual TiXmlNode* GetExtensionsNode();
    virtual void AddToExtensions( const wxString& stringDesc );
    virtual void ProjectFileRenamed( ProjectFile* pf );
    virtual FilesList& GetFilesList() { return m_Files; }
  private:
    void Open();
    void ExpandVirtualBuildTargetGroup( const wxString& alias, wxArrayString& result ) const;
    ProjectBuildTarget* AddDefaultBuildTarget();
    int IndexOfBuildTargetName( const wxString& targetName ) const;
    wxString CreateUniqueFilename();
    void NotifyPlugins( wxEventType type, const wxString& targetName = wxEmptyString, const wxString& oldTargetName = wxEmptyString );
    VirtualBuildTargetsMap m_VirtualTargets;
    BuildTargets           m_Targets;
    wxString               m_ActiveTarget;
    wxString               m_LastSavedActiveTarget;
    wxString               m_DefaultExecuteTarget;
    wxString               m_Makefile;
    bool m_CustomMakefile;
    mutable wxString       m_MakefileExecutionDir;
    std::vector<Glob> m_Globs;
    FilesList         m_Files;
    ProjectFileArray  m_FileArray;
    wxArrayString     m_ExpandedNodes;
    wxArrayString     m_SelectedNodes;
    bool              m_Loaded;
    wxTreeItemId      m_ProjectNode;
    wxArrayString m_VirtualFolders;
    bool     m_CurrentlyLoading;
    wxString m_CommonTopLevelPath;
    wxString m_BasePath;
    PCHMode m_PCHMode;
    ProjectFiles        m_ProjectFilesMap;
    ProjectBuildTarget* m_CurrentlyCompilingTarget;
    wxDateTime m_LastModified;
    bool     m_ExtendedObjectNamesGeneration;
    wxString m_Notes;
    bool     m_AutoShowNotesOnLoad;
    bool     m_CheckForExternallyModifiedFiles;
    TiXmlElement* m_pExtensionsElement;
};

wxString cbGetDynamicLinkerPathForTarget( M_Code_Project *project, ProjectBuildTarget* target );
wxString cbMergeLibPaths( const wxString &oldPath, const wxString &newPath );

#endif
